package com.store.satring.dos;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.Column;
import javax.validation.constraints.NotNull;

import java.util.Date;

import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
import javax.persistence.TypedQuery;
import javax.persistence.Version;

import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.format.annotation.DateTimeFormat;

import com.store.satring.utils.StoreUtility;

import javax.persistence.ManyToOne;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.ManyToMany;

@Configurable
@Entity
public class Invoice {

    /**
     */
    @NotNull
    @Column(unique = true)
    private Integer invoiceNo;

    /**
     */
    @NotNull
    @Temporal(TemporalType.TIMESTAMP)
    @DateTimeFormat(style = "M-")
    private Date invoiceDate;

    /**
     */
    @NotNull
    @ManyToOne
    private Customer customer;
    
    private double rate;
    
    private double area;
    
    private double amount;
    
    private double advanceAmount;

    /**
     */
    @ManyToMany(cascade = CascadeType.ALL)
    private List<ItemDetail> itemDetails = new ArrayList<ItemDetail>();

    /**
     */
    @ManyToMany(cascade = CascadeType.ALL)
    private List<ItemReturnDetail> itemReturnDetails = new ArrayList<ItemReturnDetail>();

    /**
     */
    @ManyToMany(cascade = CascadeType.ALL)
    private List<ItemBalanceDetail> itemBalanceDetails = new ArrayList<ItemBalanceDetail>();

    /**
     */
    private String siteFor;

    /**
     */
    private String description;

    /**
     */
    @Temporal(TemporalType.TIMESTAMP)
    @DateTimeFormat(style = "M-")
    private Date createdOn;

    /**
     */
    @Temporal(TemporalType.TIMESTAMP)
    @DateTimeFormat(style = "M-")
    private Date updatedOn;

    /**
     */
    private String createdBy;

    /**
     */
    private String updatedBy;

	public String toString() {
        return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE);
    }

	@PersistenceContext
    transient EntityManager entityManager;

	public static final List<String> fieldNames4OrderClauseFilter = java.util.Arrays.asList("invoiceNo", "invoiceDate", "customer", "itemDetails", "itemReturnDetails", "itemBalanceDetails", "siteFor", "description", "createdOn", "updatedOn", "createdBy", "updatedBy", "rate", "area", "amount", "advanceAmount");

	public static final EntityManager entityManager() {
        EntityManager em = new Invoice().entityManager;
        if (em == null) throw new IllegalStateException("Entity manager has not been injected (is the Spring Aspects JAR configured as an AJC/AJDT aspects library?)");
        return em;
    }

	public static long countInvoices() {
        return entityManager().createQuery("SELECT COUNT(o) FROM Invoice o Where o.customer.store = 0", Long.class).getSingleResult();
    }

	public static List<Invoice> findAllInvoices() {
        return entityManager().createQuery("SELECT o FROM Invoice o Where o.customer.store = 0", Invoice.class).getResultList();
    }

	public static List<Invoice> findAllInvoices(String sortFieldName, String sortOrder) {
        String jpaQuery = "SELECT o FROM Invoice o Where o.customer.store = 0";
        if (fieldNames4OrderClauseFilter.contains(sortFieldName)) {
            jpaQuery = jpaQuery + " ORDER BY " + sortFieldName;
            if ("ASC".equalsIgnoreCase(sortOrder) || "DESC".equalsIgnoreCase(sortOrder)) {
                jpaQuery = jpaQuery + " " + sortOrder;
            }
        }
        return entityManager().createQuery(jpaQuery, Invoice.class).getResultList();
    }

	public static Invoice findInvoice(Long id) {
        if (id == null) return null;
        return entityManager().find(Invoice.class, id);
    }

	public static List<Invoice> findInvoiceEntries(int firstResult, int maxResults) {
        return entityManager().createQuery("SELECT o FROM Invoice o  Where o.customer.store = 0", Invoice.class).setFirstResult(firstResult).setMaxResults(maxResults).getResultList();
    }

	public static Integer getNewInvoiceNo() {
		Query createNativeQuery = entityManager()
				.createNativeQuery(
						"Select invoice_no FROM invoice inv inner join customer cus on cus.id = inv.customer Where cus.store=0 Order by invoice_no desc limit 1");
		Object singleResult = null;
		try {
			singleResult = createNativeQuery.getSingleResult();
		} catch (Exception exp) {
			exp.printStackTrace();
		}
		if (singleResult == null) {
			return 1;
		}
		return ((Integer) singleResult) + 1;
	}
	
	public static List<Invoice> findInvoiceEntries(int firstResult, int maxResults, String sortFieldName, String sortOrder) {
        String jpaQuery = "SELECT o FROM Invoice o  Where o.customer.store = 0";
        if (fieldNames4OrderClauseFilter.contains(sortFieldName)) {
            jpaQuery = jpaQuery + " ORDER BY " + sortFieldName;
            if ("ASC".equalsIgnoreCase(sortOrder) || "DESC".equalsIgnoreCase(sortOrder)) {
                jpaQuery = jpaQuery + " " + sortOrder;
            }
        }
        return entityManager().createQuery(jpaQuery, Invoice.class).setFirstResult(firstResult).setMaxResults(maxResults).getResultList();
    }

	@Transactional
    public void persist() {
        if (this.entityManager == null) this.entityManager = entityManager();
        this.entityManager.persist(this);
    }

	@Transactional
    public void remove() {
        if (this.entityManager == null) this.entityManager = entityManager();
        if (this.entityManager.contains(this)) {
            this.entityManager.remove(this);
        } else {
            Invoice attached = Invoice.findInvoice(this.id);
            this.entityManager.remove(attached);
        }
    }

	@Transactional
    public void flush() {
        if (this.entityManager == null) this.entityManager = entityManager();
        this.entityManager.flush();
    }

	@Transactional
    public void clear() {
        if (this.entityManager == null) this.entityManager = entityManager();
        this.entityManager.clear();
    }

	@Transactional
    public Invoice merge() {
        if (this.entityManager == null) this.entityManager = entityManager();
        Invoice merged = this.entityManager.merge(this);
        this.entityManager.flush();
        return merged;
    }

	public Integer getInvoiceNo() {
        return this.invoiceNo;
    }

	public void setInvoiceNo(Integer invoiceNo) {
        this.invoiceNo = invoiceNo;
    }

	public Date getInvoiceDate() {
        return this.invoiceDate;
    }

	public void setInvoiceDate(Date invoiceDate) {
        this.invoiceDate = invoiceDate;
    }

	public Customer getCustomer() {
        return this.customer;
    }

	public void setCustomer(Customer customer) {
        this.customer = customer;
    }

	public List<ItemDetail> getItemDetails() {
        return this.itemDetails;
    }

	public void setItemDetails(List<ItemDetail> itemDetails) {
        this.itemDetails = itemDetails;
    }

	public List<ItemReturnDetail> getItemReturnDetails() {
        return this.itemReturnDetails;
    }

	public void setItemReturnDetails(List<ItemReturnDetail> itemReturnDetails) {
        this.itemReturnDetails = itemReturnDetails;
    }

	public String getSiteFor() {
        return this.siteFor;
    }

	public void setSiteFor(String siteFor) {
        this.siteFor = siteFor;
    }

	public String getDescription() {
        return this.description;
    }

	public void setDescription(String description) {
        this.description = description;
    }

	public Date getCreatedOn() {
        return this.createdOn;
    }

	public void setCreatedOn(Date createdOn) {
        this.createdOn = createdOn;
    }

	public Date getUpdatedOn() {
        return this.updatedOn;
    }

	public void setUpdatedOn(Date updatedOn) {
        this.updatedOn = updatedOn;
    }

	public String getCreatedBy() {
        return this.createdBy;
    }

	public void setCreatedBy(String createdBy) {
        this.createdBy = createdBy;
    }

	public String getUpdatedBy() {
        return this.updatedBy;
    }

	public void setUpdatedBy(String updatedBy) {
        this.updatedBy = updatedBy;
    }

	public static Long countFindInvoicesByInvoiceNoEquals(Integer invoiceNo) {
        if (invoiceNo == null) throw new IllegalArgumentException("The invoiceNo argument is required");
        EntityManager em = Invoice.entityManager();
        TypedQuery q = em.createQuery("SELECT COUNT(o) FROM Invoice AS o WHERE  Where o.customer.store = 0 and o.invoiceNo = :invoiceNo", Long.class);
        q.setParameter("invoiceNo", invoiceNo);
        return ((Long) q.getSingleResult());
    }

	public static List<Invoice> findInvoicesByInvoiceNoEquals(String searchData) {
        if (searchData == null) throw new IllegalArgumentException("The argument is required");
        
        EntityManager em = Invoice.entityManager();
        String jpaQuery = " select inv.id from invoice inv " +
        				  " inner join customer cus on cus.id = inv.customer and cus.store = 0 "+
        				  "	where  inv.invoice_no like '%"+searchData+"%' "
        				  + " OR cus.address like '%"+searchData+"%' "
        				  + " OR cus.name like '%"+searchData+"%' "
        				  + "OR cus.mobile_no like '%"+searchData+"%'  "
        				  + "OR inv.site_for like '%"+searchData+"%'";
        
        List resultList = em.createNativeQuery(jpaQuery).getResultList();
        List<Invoice> invoices = new ArrayList<Invoice>();
        for (Object object : resultList) {
        	invoices.add(Invoice.findInvoice(Long.parseLong(object.toString())));
		}
        return invoices;
    }

	public static Invoice findStoreInvoice() {
		EntityManager em = Invoice.entityManager();
		String jpaQuery = " select inv.id from invoice inv "
				+ " inner join customer cus on cus.id = inv.customer  "
				+ "	where cus.store = 1";
		List resultList = em.createNativeQuery(jpaQuery).getResultList();
		if (resultList.size() > 1) {
			throw new IllegalStateException(
					"More than one Invoice exists in the system with store value 1. Its an illegal State. Please contact to your admin.");
		} else if (resultList.size() == 0) {
			return null;
		}
		return Invoice.findInvoice(Long.parseLong(resultList.get(0).toString()));
	}

	@Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long id;

	@Version
    @Column(name = "version")
    private Integer version;

	public Long getId() {
        return this.id;
    }

	public void setId(Long id) {
        this.id = id;
    }

	public Integer getVersion() {
        return this.version;
    }

	public void setVersion(Integer version) {
        this.version = version;
    }

	public List<ItemBalanceDetail> getItemBalanceDetails() {
		return itemBalanceDetails;
	}

	public void setItemBalanceDetails(List<ItemBalanceDetail> itemBalanceDetails) {
		this.itemBalanceDetails = itemBalanceDetails;
	}

	public double getRate() {
		return rate;
	}

	public void setRate(double rate) {
		this.rate = StoreUtility.getAmount(2, rate);
	}

	public double getArea() {
		return area;
	}

	public void setArea(double area) {
		this.area = StoreUtility.getAmount(2, area);
	}

	public double getAmount() {
		return amount;
	}

	public void setAmount(double amount) {
		this.amount = StoreUtility.getAmount(2, amount);
	}

	public double getAdvanceAmount() {
		return advanceAmount;
	}

	public void setAdvanceAmount(double advanceAmount) {
		this.advanceAmount = StoreUtility.getAmount(2, advanceAmount);
	}

}
